#pragma once

// Interfaces for InputVariableSetter plugins and plugin factories.

#include "envoy/common/pure.h"
#include "envoy/config/typed_config.h"

#include "nighthawk/adaptive_load/config_validator.h"

#include "external/envoy/source/common/protobuf/protobuf.h"

#include "api/client/options.pb.h"

#include "absl/status/status.h"

namespace Nighthawk {

/**
 * An interface for plugins that apply a StepController-computed input value to a CommandLineOptions
 * proto. This may entail setting a numeric proto field directly, setting the value in a header, or
 * otherwise manipulating the proto to reflect the number.
 *
 * See source/adaptive_load/input_variable_setter_impl.h for example plugins.
 */
class InputVariableSetter {
public:
  virtual ~InputVariableSetter() = default;
  /**
   * Applies the numeric input value to the CommandLineOptions proto.
   *
   * @param command_line_options A Nighthawk Service CommandLineOptions load spec template with most
   * fields already filled.
   * @param input_value A value generated by a StepController to be applied to the next load spec.
   *
   * @return Status OK if the value could be applied, or an error if it could not be applied (e.g.
   * out of range).
   */
  virtual absl::Status SetInputVariable(nighthawk::client::CommandLineOptions& command_line_options,
                                        double input_value) PURE;
};

using InputVariableSetterPtr = std::unique_ptr<InputVariableSetter>;

/**
 * A factory that must be implemented for each InputVariableSetter plugin. It instantiates the
 * specific InputVariableSetter class after unpacking the plugin-specific config proto.
 */
class InputVariableSetterConfigFactory : public Envoy::Config::TypedFactory,
                                         public ConfigValidator {
public:
  std::string category() const override { return "nighthawk.input_variable_setter"; }
  /**
   * Instantiates the specific InputVariableSetter class. Casts |message| to Any, unpacks it to the
   * plugin-specific proto, and passes the strongly typed proto to the plugin constructor.
   *
   * @param message Any typed_config proto taken from the TypedExtensionConfig.
   *
   * @return InputVariableSetterPtr Pointer to the new plugin instance.
   *
   * @throw Envoy::EnvoyException If the Any proto cannot be unpacked as the type expected by the
   * plugin.
   */
  virtual InputVariableSetterPtr
  createInputVariableSetter(const Envoy::Protobuf::Message& message) PURE;
};

} // namespace Nighthawk
